#!/usr/bin/python
#encoding=utf8
#author:linluxiang@gmail.com
#version 1.0.0

'''
这个程序的作用是分析一个CodeObject并执行它。
'''
import symbols
import threading
import StringIO
from symbols import Node as Node
from symbols import CodeObject as Code
from symbols import Stack as Stack
import sys
import pickle
stack=Stack()
code=None
pc=0
jumped=False
ready=False
userIn=StringIO.StringIO()
userOut=StringIO.StringIO()
guiin=[]
complete=False
def Error(str):
    global msg,pc,complete
    print>>userOut,str
    complete=True
    sys.exit(1)

#-----GetFunc的目的就是分配函数----
def GetFunc(str):
    if str=='PUSH_DATA':return PUSH_DATA
    elif str=='WRITE':return WRITE
    elif str=='LOAD_NAME':return LOAD_NAME
    elif str=='STORE_NAME':return STORE_NAME
    elif str=='POP_TOP':return POP_TOP
    elif str=='MAKE_ARRAY':return MAKE_ARRAY
    elif str=='MAKE_NAME':return MAKE_NAME
    elif str=='PUSH_NAME':return PUSH_NAME
    elif str=='CAL_DATA':return CAL_DATA
    elif str=='MAKE_SIGN':return MAKE_SIGN
    elif str=='READ':return READ
    elif str=='COMPARE':return COMPARE
    elif str=='PASS':return PASS
    elif str=='JUMP_IF_FALSE':return JUMP_IF_FALSE
    elif str=='JUMP':return JUMP
#----------PASS就是什么也不做--------
def PASS(n):
    pass
#----------POP_TOP就是弹出栈顶元素-----
def POP_TOP(n):
    global stack
    stack.pop()
#----------PUSH_NAME就是将name推入堆栈------
def PUSH_NAME(name):
    global stack,code
    stack.push(name)
#----------JUMP就是无条件跳转-------------
def JUMP(num):
    global pc,stack,jumped
    pc=num-1#num-1而不是num的原因是，pc在执行完毕以后会+1.
    #jumped=false
#----------JUMP_IF_FALSE函数就是将程序跳转到指定code执行---
def JUMP_IF_FALSE(num):
    global pc,stack,jumped
    a=stack.pop()
    #PRINT( 'jp if f',a)
    if a==False:
        pc=num-1
        #jumped=True
#----------MAKE_SIGN就是将栈顶的数根据传入的符号参数修改它的正负号-----
def MAKE_SIGN(op):
    global stack,name
    if op[1]=='-':
        a=stack.pop()
        a=(a[0],-a[1],a[2])
        stack.push(a)
#----------COMPARE就是比较栈顶和次栈顶的2个元素，把入栈--------
def COMPARE(op):
    global stack,name
    b=stack.pop()
    a=stack.pop()
    b=str(b[1])
    a=str(a[1])
    val=eval(''.join([a,op[1],b]))
    stack.push(val)
#----------CAL_DATA就是接受一个op参数，表示运算符，进行计算----
def CAL_DATA(op):
    global stack,code
    b=stack.pop()
    a=stack.pop()
    if op[1]=='*':
        if b[0]=='Integer' and a[0]=='Integer':
            val=('Integer',a[1]*b[1],a[2])
        else:
            val=('Real',a[1]*b[1],a[2])
    elif op[1]=='/':
        if a[0]=='Integer' and b[0]=='Integer':
            if b[1]==0:
                Error('Devide 0 error.')
            else:
                val=('Integer',a[1]/b[1],a[2])
        else:
            if b[1]==0 or b[1]==0.0:
                Error('Device 0 error.')
            else:
                val=('Real',a[1]/b[1],a[2])
    elif op[1]=='+':
        if b[0]=='Integer' and a[0]=='Integer':
            val=('Integer',a[1]+b[1],a[2])
        else:
            val=('Real',a[1]+b[1],a[2])
    elif op[1]=='-':
        if b[0]=='Integer' and a[0]=='Integer':
            val=('Integer',a[1]-b[1],a[2])
        else:
            val=('Real',a[1]-b[1],a[2])

    stack.push(val)
#----------MAKE_NAME就是初始化一个name------
def MAKE_NAME(id):
    global code
    if code.getNameType(id)=='Integer':
        code.setNameValue(id,('Integer',0,0))
    else:
        code.setNameValue(id,('Real',0.0,0))
#----------MAKE_ARRAY就是将一个name变成数组，设置其大小----
def MAKE_ARRAY(id):
    global stack,code
    name=id[0]
    dimention=id[1]
    size=[]
    for a in xrange(dimention):
        b=stack.pop()
        if b[0]!='Integer':
            Error('Array index must be a type of integer.')

        size.append(b[1])
    size=size[::-1]
    code.setNameSize(name,tuple(size))
    

#------WRITE指令为了打印栈顶的name-----
def WRITE(c):
    global stack,code
    if c==0:
        if stack.top()!=None:
            print >>userOut,stack.pop()[1]
        else:
            Error('Wrong value of write function.')
            
#------READ指令为了读取name-------
def READ(name):
    global stack,code,ready,guiin
    if code.hasName(name):
        #PRINT( name)
        pos=[]
        for i in xrange(code.getNameSize(name).__len__()):
            temp=stack.pop()
            if temp[0]=='Integer':
                pos.append(temp[1])
            else:
                Error('Array index must be a type of integer.')
        pos=tuple(pos[::-1])
        if __name__=='__main__':#如果是直接运行
            value=raw_input('Input value:')
        else:#作为模块调用
            ready=True

            while guiin.__len__()==0:pass

            value=guiin[0]
            del guiin[0]

            ready=False

        if code.getNameType(name)=='Integer':
            value=('Integer',int(value),name['type'])
            code.setNameValue(name,value,pos)
        else:
            value=('Real',float(value),name['type'])
            code.setNameValue(name,value,pos)
    else:
        Error("Not defined identifier %s in line %s"%(name['name'],name['line']))
#-----PUSH_DATA指令讲data推入栈顶-------
def PUSH_DATA(data):
    global stack
    stack.push(data)
#-----STORE_NAME指令将栈顶元素赋值给指定name，注意需要判断name的类型,还有array的溢出。
def STORE_NAME(name):
    global code,stack
    value=stack.pop()
    #PRINT( name)
    if code.hasName(name):
        if code.getNameType(name)==value[0]:
            pos=[]
            zero=[]
            for i in xrange(code.getNameSize(name).__len__()):
                temp=stack.pop()
                if temp[0]=='Integer':
                    pos.append(temp[1])
                    zero.append(0)
                else:
                    Error('Array index must be a type of integer.')
            pos=tuple(pos[::-1])
            zero=tuple(zero)
            if pos<code.getNameSize(name) and pos>=zero:
                code.setNameValue(name,value,pos)
            else:
                Error('Index out of range in line %s.'%(name['line']))
        else:
            if code.getNameType(name)=='Real' and value[0]=='Integer':
                pos=[]
                zero=[]
                for i in xrange(code.getNameSize(name).__len__()):
                    temp=stack.pop()
                    if temp[0]=='Integer':
                        pos.append(temp[1])
                        zero.append(0)
                    else:
                        Error('Array index must be a type of integer.')
                pos=tuple(pos[::-1])
                zero=tuple(zero)

                if pos<code.getNameSize(name)and pos>=zero:
                    code.setNameType(name,'Real')
                    value=ConvertIntegerToReal(value)
                    code.setNameValue(name,value,pos)
                else:
                    Error('Index out of range in line %s.'%(name['name']))
            else:
                Error("Type error,ID %s's type is %s while get %s in line %s."%(name['name'],code.getNameType(name),value[0],name['line']))

    else:
        Error("Not defined identifier %s in line %s"%(name['name'],name['line']))
    stack.push(value)

#------LOAD_NAME代码就是将指定的name的值放到堆栈的顶端-----
def LOAD_NAME(name):
    global code,stack
    if code.hasName(name):
        if not stack.isEmpty():
            pos=[]
            zero=[]
            for i in xrange(code.getNameSize(name).__len__()):
                temp=stack.pop()
                if temp[0]=='Integer':
                    pos.append(temp[1])
                    zero.append(0)
                else:
                    Error('Array index must be a type of integer.')
            pos=tuple(pos[::-1])
            zero=tuple(zero)
            if pos<code.getNameSize(name) and pos>=zero:
                stack.push(code.getNameValue(name,pos))
            else:
                Error('Index out of range in line %s.'%(name['line']))
        else:

            Error('Array index must be a type of integer.')
    else:
        Error("Not defined identifier %s in line %s."%(name['name'],name['line']))
def ConvertIntegerToReal(value):
    global code,stack
    return ('Real',0.0+value[1],value[2])

def Run(filename):
    global code,stack,pc,msg,jumped,userIn,userOut,complete
    stack=Stack()
    code=None
    pc=0
    jumped=False
    msg=[]
    complete=False
    try:
        f=open(filename)
        code=pickle.load(f)
        codes=code.getCodes()
        i=0
    #反注释以下几行代码可以看见中间代码
    #    for x in codes:
    #        print i,x
    #        i+=1
        if __name__=='__main__':
            userIn=sys.stdin
            userOut=sys.stdout
        length=code.getCodeAmount()
        while pc < length:
            asm=code.getCode(pc)
            #print asm
            GetFunc(asm[0])(asm[1])
            pc+=1
        complete=True
        f.close()
    except:
        Error("Cannot find %s!"%(filename))

def main():
    global code,stack,pc
    Run('new.cmmc')

if __name__=='__main__':
    main()
   
